package com.solution.common.redis;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Repository;

/**
 * Redis缓存操作
 * 
 * @author yaoyu
 * @date 2017年10月27日 下午4:21:16
 */
@Repository
public class RedisCacheClient {

	private static final Logger logger = LoggerFactory.getLogger(RedisCacheClient.class);

	/**
	 * redis 操作模板
	 */
	@Autowired
	private RedisTemplate<String, Object> redisTemplate;

	/**
	 * 清除单个缓存
	 * 
	 * @param key
	 * @author yaoyu
	 * @date 2017年10月27日 下午4:21:30
	 */
	public Long evictOneKey(Object key) {
		final String keyStr = key.toString();
		logger.info("清空redis key : {}", keyStr);
		Long result = redisTemplate.execute(new RedisCallback<Long>() {
			public Long doInRedis(RedisConnection connection) throws DataAccessException {
				return connection.del(keyStr.getBytes());
			}
		});
		return result;
	}

	/**
	 * 批量模糊清除缓存
	 * 
	 * @param keyPrefix
	 *            模糊匹配的key前缀
	 * @author yaoyu
	 * @date 2017年10月27日 下午4:21:53
	 */
	public Long evictBatchKeys(final String keyPrefix) {
		logger.info("批量模糊清除redis key : {}", keyPrefix);
		Long result = redisTemplate.execute(new RedisCallback<Long>() {
			public Long doInRedis(RedisConnection connection) throws DataAccessException {
				connection.select(1);
				// 先查出所有模糊匹配的key
				Set<byte[]> keys = connection.keys((keyPrefix + "*").getBytes());
				// 然后逐个清除
				Long count = 1L;
				if (keys != null) {
					for (byte[] key : keys) {
						count += connection.del(key);
					}
				}
				return count;
			}
		});
		return result;
	}

	/**
	 * 获取redis中指定hash中的key的value
	 * @param key
	 * @param hashKey
	 * @return
	 * @author xkl
	 * @date 2018年5月5日 下午4:05:28
	 */
	public Long getHashValue(final String key, final String hashKey) {
		Long result = redisTemplate.execute(new RedisCallback<Long>() {
			@Override
			public Long doInRedis(RedisConnection connection) throws DataAccessException {
				connection.select(1);
				if (connection.hExists(key.getBytes(), hashKey.getBytes())) {
					byte[] byteArr = connection.hGet(key.getBytes(), hashKey.getBytes());
					return Long.parseLong(new String(byteArr));
				} else {
					return 0l;
				}
			}
		});
		return result;
	}

	/**
	 * 使指定hash中key的value值加1
	 * @param key
	 * @param hashKey
	 * @return
	 * @author xkl
	 * @date 2018年5月5日 下午4:05:58
	 */
	public Long doHashIncrby(final String key, final String hashKey) {
		return doHashIncrby(key, hashKey, 1);
	}

	/**
	 * 使指定hash中key的value值增量
	 * @param key
	 * @param hashKey
	 * @param increment
	 * @return
	 * @author xkl
	 * @date 2018年6月12日 上午10:09:36
	 */
	public Long doHashIncrby(final String key, final String hashKey, final Integer increment){
		Long result = redisTemplate.execute(new RedisCallback<Long>() {
			public Long doInRedis(RedisConnection connection) throws DataAccessException {
				connection.select(1);
				if (connection.hExists(key.getBytes(), hashKey.getBytes())) {
					return connection.hIncrBy(key.getBytes(), hashKey.getBytes(), increment);
				} else {
					byte[] one = "1".getBytes();
					boolean isOk = connection.hSet(key.getBytes(), hashKey.getBytes(), one);
					if (isOk) {
						return 1l;
					} else {
						return 0l;
					}
				}
			};
		});
		return result;
	}

	/**
	 * 获取指定hash中key的集合
	 * @param key
	 * @return
	 * @author xkl
	 * @date 2018年5月17日 下午3:11:34
	 */
	public Set<String> getHashKeys(final String key){
		Set<String> keysSet = redisTemplate.execute(new RedisCallback<Set<String>>() {
			@Override
			public Set<String> doInRedis(RedisConnection connection) throws DataAccessException {
				connection.select(1);
				Set<byte[]> keySet = connection.hKeys(key.getBytes());
				Set<String> keysSet = new HashSet<String>();
				for (byte[] bs : keySet) {
					keysSet.add(new String(bs));
				}
				return keysSet;
			}
		});
		return keysSet;
	}

	/**
	 * 移除指定hash中的key-value
	 * @param key
	 * @return
	 * @author xkl
	 * @date 2018年5月23日 下午4:35:15
	 */
	public Long removeHashKeys(final String key, final List<String> hashKeys){
		Long result = redisTemplate.execute(new RedisCallback<Long>() {
			public Long doInRedis(RedisConnection connection) throws DataAccessException {
				connection.select(1);
				for (String hashKey : hashKeys) {
					if (connection.hExists(key.getBytes(), hashKey.getBytes())) {
						connection.hDel(key.getBytes(), hashKey.getBytes());
					}
				}
				return 1L;
			};
		});
		return result;
	}

	/**
	 * 增加指定的key-value
	 * @param key
	 * @param hashKey
	 * @param hashValue
	 * @return
	 * @author xkl
	 * @date 2018年6月26日 下午4:53:36
	 */
	public boolean doSet(final String key, final String value){
		Boolean result = redisTemplate.execute(new RedisCallback<Boolean>() {
			public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
				connection.select(1);
				byte[] val = value.getBytes();
				connection.set(key.getBytes(), val);
				return true;
			};
		});
		return result;
	}
	
	/**
	 * 设置超时时间
	 * @param key
	 * @param timeOut
	 * @return
	 * @author xkl
	 * @date 2018年6月26日 下午5:04:35
	 */
	public boolean doExpire(final String key, final long timeOut){
		Boolean result = redisTemplate.execute(new RedisCallback<Boolean>() {
			public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
				connection.select(1);
				connection.expire(key.getBytes(), timeOut);
				return true;
			};
		});
		return result;
	}
	
	/**
	 * 获取指定key的value
	 * @param key
	 * @return
	 * @author xkl
	 * @date 2018年6月26日 下午5:15:13
	 */
	public String doGet(final String key){
		String result = redisTemplate.execute(new RedisCallback<String>() {
			public String doInRedis(RedisConnection connection) throws DataAccessException {
				connection.select(1);
				byte[] val = connection.get(key.getBytes());
				if(val != null && val.length > 0){
					return new String(val);
				}else{
					return null;
				}
			};
		});
		return result;
	}
}
